﻿#region Imports
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using DataAnnotationsContrib;
using DataAnnotationsContrib.Interfaces;
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endregion

namespace DataAnnotationsContrib.Tests.Framework
{
    /// <summary>
    /// Serves as abase clas for all algorithm tests. Performs basic minimal test functionality
    /// by exercising an IValidationAlgorithm with the passed valid and invalid values.
    /// </summary>
    [TestClass]
    public abstract class AttributeTest<TAttribute, TAttributedObject> : Test
        where TAttribute : ValidationAttribute
        where TAttributedObject : new()
    {
        #region Infrastructure
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // [TestCleanup()]
        // public void MyTestCleanup() { }

        private Type AttributedObjectType { get; set; }
        private string AttributedObjectPropertyName { get; set; }
        private IList<string> ValidValues { get; set; }
        private IList<string> InvalidValues { get; set; }

        protected AttributeTest(string attributedObjectPropertyName,
            IList<string> validValues, IList<string> invalidValues)
        {
            Assert.IsFalse(String.IsNullOrEmpty(attributedObjectPropertyName), "attributedObjectPropertyName must not be null or ''");
            Assert.IsNotNull(validValues, "validValues must be supplied.");
            Assert.AreNotEqual(0, validValues.Count, "validValues must not contain more 0 items.");
            Assert.IsNotNull(validValues, "invalidValues must be supplied.");
            Assert.AreNotEqual(0, invalidValues.Count, "validValues must not contain more 0 items.");

            this.AttributedObjectType = typeof(TAttributedObject);
            this.AttributedObjectPropertyName = attributedObjectPropertyName;
            this.ValidValues = validValues;
            this.InvalidValues = invalidValues;
        }

        private TAttributedObject CreateAttributedObject()
        {
            return new TAttributedObject();
        }
        private void SetAttributedObjectProperty(TAttributedObject target, object propertyValue)
        {
            Type t = typeof(TAttributedObject);
            t.GetProperty(this.AttributedObjectPropertyName).SetValue(target, propertyValue, null);
        }

        private bool CallValidateOnAttributedProperty(TAttributedObject target)
        {
            IList<ValidationAttribute> appliedAttributes = this.AttributedObjectType.GetValidationAttributes(this.AttributedObjectPropertyName);

            bool attribFound = (appliedAttributes != null && appliedAttributes.Count == 1);
            if (!attribFound) Assert.Inconclusive(String.Format("A single instance of TAttribute is not applied to property {0}, cannot perform test.", this.AttributedObjectPropertyName));

            ValidationAttribute attribute = appliedAttributes[0] as ValidationAttribute;

            return attribute.IsValid(target.GetPropertyValue(this.AttributedObjectPropertyName));
        }
        #endregion

        #region Tests
        public void TestAllValidValuesShouldValidateTrue()
        {
            TestHelper.Trace(this.TestContext, this.ValidValues);

            var target = CreateAttributedObject();
            foreach (var value in this.ValidValues)
            {
                SetAttributedObjectProperty(target, value);
                Assert.IsTrue(CallValidateOnAttributedProperty(target), String.Format("Value '{0}' unexpectedly failed validation.", value));
            }
        }

        public void TestAllInvalidValuesShouldValidateFalse()
        {
            TestHelper.Trace(this.TestContext, this.InvalidValues);

            var target = CreateAttributedObject();
            foreach (var value in this.InvalidValues)
            {
                SetAttributedObjectProperty(target, value);
                Assert.IsFalse(CallValidateOnAttributedProperty(target), String.Format("Value '{0}' unexpectedly passed validation.", value));
            }
        }
        #endregion
    }
}
